/*
 * 平台相关代码
 *
 * author: lishutong
 * site: https://lishutong1024.cn / https://lishutong1024.github.cn
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "os_sys.h"
#include "bsp_usart.h"
#include "gd32f4xx.h"                   // Device header

/**
 * 任务运行环境初始化
 * @param task 等待初始化的任务
 */
void os_task_ctx_init(struct _os_task_t *task, void * stack_start, int stack_size, void (*entry)(void *), void * param) {
    // 这里要对齐，只使用基本寄存器的栈，不含VFP
    uint32_t stack_top = (uint32_t)stack_start + stack_size - sizeof(os_stack_base_frame_t);
    os_stack_base_frame_t * frame = (os_stack_base_frame_t *)stack_top;
    
    frame->r0 = (cpu_stack_t)param; // 传给任务的入口函数
    frame->r1 = 0x1;
    frame->r2 = 0x2;
    frame->r3 = 0x3;

    frame->r12 = 0x12;
#if OS_TASK_EXIT_ENABLE
    frame->r14_lr = (cpu_stack_t)os_task_exit; // 返回地址
#else
    frame->r14_lr = 0x14;
#endif
    frame->pr15_pc = (cpu_stack_t)entry;
    frame->xpsr = (1 << 24); // 设置了Thumb模式，恢复到Thumb状态而非ARM状态运行

    // 以下的有些寄存器给出了默认的数值，方便在IDE中调试观察效果
    os_task_ctx_t *ctx = &task->ctx;
    ctx->r4 = 0x4;
    ctx->r5 = 0x5;
    ctx->r6 = 0x6;
    ctx->r7 = 0x7;
    ctx->r8 = 0x8;
    ctx->r9 = 0x9;
    ctx->r10 = 0x10;
    ctx->r11 = 0x11;    
    ctx->r13 = (cpu_stack_t)frame;

    ctx->flags = 1 << 4;        // 当前任务未使用FPU
}

/**
 * 切换到首个任务
 */
void os_task_switch_to (os_task_ctx_t * to) {
    register os_task_ctx_t * to_ctx __asm("r0") = to;
 
    // 清除CONTROL.FPCA位，目前未使用FPU
    uint32_t control = __get_CONTROL();
    control &= ~(1 << 4);
    __set_CONTROL(control);
    
    __asm volatile("cpsie i\nsvc #0x00"::"r"(to_ctx));
}

os_task_ctx_t * int_ctx_from, * int_ctx_to;

/**
 * 从一个任务切换到另一个任务
 */
void os_task_switch(os_task_ctx_t *from, os_task_ctx_t *to) {
    int_ctx_from = from;
    int_ctx_to = to;
    SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}

void os_task_int_switch (os_task_ctx_t *from, os_task_ctx_t *to) {
    int_ctx_from = from;
    int_ctx_to = to;
    SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}

// 在任务切换中，主要依赖了PendSV进行切换。PendSV其中的一个很重要的作用便是用于支持RTOS的任务切换。
// 实现方法为：
// 1、首先将PendSV的中断优先配置为最低。这样只有在其它所有中断完成后，才会触发该中断；
//    实现方法为：向NVIC_SYSPRI2写NVIC_PENDSV_PRI
// 2、在需要中断切换时，设置挂起位为1，手动触发。这样，当没有其它中断发生时，将会引发PendSV中断。
//    实现方法为：向NVIC_INT_CTRL写NVIC_PENDSVSET
// 3、在PendSV中，执行任务切换操作。
void SysTick_Handler(void) {
    os_int_enter();
    os_sched_time_tick();
    os_int_leave();
}

/**
 * 关中断保护
 */
os_int_status_t os_plat_int_disable (void) {
    os_int_status_t primask = __get_PRIMASK();
    __disable_irq();        // CPSID I
    return primask;
}

/**
 * 恢复原有中断
 */
void os_plat_int_restore (os_int_status_t status) {
    __set_PRIMASK(status);
}

/**
 * 系统平台初始化
 */
void os_plat_init(void) {   
    // 关中断，初始化过程应当禁止中断响应
    __disable_irq();        
    
    // 设置系统时钟节拍，每隔OS_SYSTICK_MS中断一次
    SystemCoreClockUpdate();
    SysTick_Config(OS_SYSTICK_MS * SystemCoreClock / 1000 - 1);

    // 设置成最低优化级
    NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
    NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
}

/**
 * 空闲任务回调
 */
void os_task_idle_hook (void) {
    //__WFI();
}


void os_plat_putc (char c) {
    usart_send_data(c);
}

/**
 * 增加系统时钟节拍计数
 */
void os_plat_ticks_update(os_ticks_t * ticks, int method) {
    if (method) {
        *ticks += 1;
    } else {
        *ticks = 0;
    }
}